package edu.upenn.seas.peerreview.pr

import edu.upenn.seas.peerreview.{Id, Source, Sink}
import edu.upenn.seas.peerreview.pr.PRMessages._

import scala.collection.mutable.Queue

/* ********************************** */
// Handles logging and auditing.

class Log {
  var Seq : SeqNum = 0
  type LogEntry = (SeqNum, LogMessage)
  val log : Queue[LogEntry] = new Queue()
  val debug=edu.upenn.seas.peerreview.Debug.debug("log")(_)
 //change this to "==" if we want audit statements to show

  def addRecv(from: Id, entry : Message, idSent: SeqNum) : SeqNum = {
    log += (Seq, LOG_RECV(from, entry, idSent))
    Seq += 1
    Seq
  }

  def addSent(to: Id, entry: Message) : SeqNum= {
    log += (Seq, LOG_SEND(to, entry))
    Seq += 1
    Seq
  }
  
  // put here for now. may also need to be passed HandlerFactory when
  // non-trivial.
  def audit(f: (Sink.T) => Source.T) : Boolean = {
    
    /*
     * Ways an audit can fail:
     * 1) expected to not send another message, but it did. (send instead of recv)
     * 2) expected to send a message, but it didn't. (recv instead of send)
     * 3) sent the wrong message
     */
    var testLog = log.clone()
    for(i <- 1 to 5) {
      val f = testLog.dequeue._2
      f match {
        case LOG_SEND(a, b) => debug("MSG SENT: to " + a + " with " + b.toString)
        case LOG_RECV(a, b, c) => debug("MSG RECV: from " + a + " with " + b.toString)
      }
    }
    val newLog = log.clone()
    val s = new Sink.T {
      var correct: Boolean = true
      def apply(to: Id, m: Message) {
        // check if that message was the next one..
        if(m.isInstanceOf[PRUIMessage]) return
        if(newLog.isEmpty) return
        val nextEntry: LogEntry = newLog.dequeue
        val nextMsg: LogMessage = nextEntry._2
        nextMsg match {
          // if it's the same as (to, m), we're done
          // if not, audit should return false
          case LOG_SEND(a, b) if a==to && b==m => {
            // do nothing, keep rolling
            debug("GOOD SEND: To: "+a+" MSG: "+b.toString)
          }
          case LOG_SEND(a, b) => {
            debug("Wrong recipient/msg")
            debug("TO: " + to + " MSG: " + m.toString)
            debug("EXPECTED TO: " + a + " MSG: " + b.toString)
            correct = false
          }
          case LOG_RECV(a, b, c) =>{
            debug("Sent new message when receive expected.")
            debug("TO: " + to + " MSG: " + m.toString)
            debug("EXPECTED FROM: " + a + " MSG: " + b.toString)
            correct = false// "return" false
          }
        } //end match
      }
    } //end sink
    
    // should be SM, but "sends" messages through s instead of Transport 
    val h = f(s) 
    //start dequeuing messages
    //if recv message, send it through the handler to SM
    // if sent message, something's wrong => return false
    // if queue empties, then everything checks out => return true
    while(!(newLog isEmpty)) {
      if(s.correct == false)
        return false
      if(log isEmpty) return true
      val entry : LogEntry = newLog.dequeue
      val msg : LogMessage = entry._2
      msg match {
        case LOG_SEND(a, b) => {
          debug("SM should have sent a message by now.")
          debug("EXPECTED TO: " + a + "MSG: " + b.toString)
          return false
        }
        case LOG_RECV(from, msg, id) =>{
          debug("GOOD RECEIVE: from: "+from+" msg: " + msg.toString)
          h(from, msg)
        }
        case _ => error("something in the log that doesn't belong")
      }
    } //end loop
    true  //our return
  } //end audit function
}
